package com.flyxteam.commons.dao.mybatis.core.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.flyxteam.commons.dao.mybatis.api.condition.Condition;
import com.flyxteam.commons.dao.mybatis.core.annotation.Column;
import com.flyxteam.commons.dao.mybatis.core.annotation.PK;
import com.flyxteam.commons.dao.mybatis.core.annotation.Table;
import com.flyxteam.commons.dao.mybatis.core.annotation.Transient;

/**
 * 对象、sql字段转换工具类
 * @author yuzh
 *
 */
public final class EntityUtil {

	private EntityUtil() {}
	
	/**
	 * 实体转换成map
	 * @param o 转换前的实体
	 * @param m 转换后的map
	 * @param isCamel 是否做camel命名规则转换
	 * @return
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	public static void entity2Map(Object o, Map<String, Object> m, boolean isCamel) throws IllegalArgumentException, IllegalAccessException {
		Class<? extends Object> c = o.getClass();
		Class<?> superclass = c.getSuperclass();
		if(!(superclass instanceof Object)) {
			entity2Map(superclass, m, isCamel);
		}
		
		Field[] declaredFields = c.getDeclaredFields();
		String columnName;
		Object columnValue;
		for (Field field : declaredFields) {
			if(field.isAnnotationPresent(Transient.class)) {
				continue;
			}
			if(!field.isAnnotationPresent(Column.class)) {
				continue;
			}
			
			// 获取字段名
			columnName = getColumnName(field, isCamel);
			
			boolean accessible = field.isAccessible();
			field.setAccessible(true);
			columnValue = field.get(o);
			
			if(columnValue != null) {
				m.put(columnName, columnValue);
			} else {
				if(!field.getDeclaredAnnotation(Column.class).useDbDefaultValue()) {
					m.put(columnName, columnValue);
				}
			}
			
			field.setAccessible(accessible);
		}
	}
	
	/**
	 * 实体集合转换map集合
	 * @param ol 转换前额实体集合
	 * @param l 转换后的map集合
	 * @param isCamel 是否做camel命名规则转换
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * 
	 * TODO 需重构
	 */
	public static <T> void entityList2Map(List<T> ol, List<Map<String, Object>> l, boolean isCamel) throws IllegalArgumentException, IllegalAccessException {
		Map<String, Object> m;
		for (Object o : ol) {
			m = new HashMap<String, Object>();
			entity2Map(o, m, isCamel);
			l.add(m);
		}
	}
	
	/**
	 * map转实体
	 * @param m
	 * @param t
	 * @param isCamel 是否做camel命名规则转换
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	public static <T> void map2Entity(Map<String, Object> m, T t, boolean isCamel) throws IllegalArgumentException, IllegalAccessException {
		Class<? extends Object> c = t.getClass();
		Class<?> superclass = c.getSuperclass();
		if(!(superclass instanceof Object)) {
			map2Entity(m, t, isCamel);
		}
		
		Field[] declaredFields = c.getDeclaredFields();
		String columnName;
		for (Field field : declaredFields) {
			if(field.isAnnotationPresent(Transient.class)) {
				continue;
			}
			if(!field.isAnnotationPresent(Column.class)) {
				continue;
			}
			
			// 获取字段名
			columnName = getColumnName(field, isCamel);
			if(m.get(columnName) != null) {
				boolean accessible = field.isAccessible();
				field.setAccessible(true);
				field.set(t, m.get(columnName));
				field.setAccessible(accessible);
			}
		}
	}
	
	/**
	 * map转实体
	 * @param ml
	 * @param tl
	 * @param c 实体类型
	 * @param isCamel 是否做camel命名规则转换
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public static <T> void mapList2Entity(List<Map<String, Object>> ml, List<T> tl, Class<T> c, boolean isCamel) throws InstantiationException, IllegalAccessException {
		T t = null;
		for (Map<String, Object> m : ml) {
			t = c.newInstance();
			map2Entity(m, t, isCamel);
			tl.add(t);
		}
	}
	
	/**
	 * 实体转换成条件对象列表
	 * @param o
	 * @param isCamel 是否做camel命名规则转换
	 * @return
	 * @throws IllegalAccessException 
	 * @throws IllegalArgumentException 
	 */
	public static List<Condition> entity2Condition(Object o, boolean isCamel) throws IllegalArgumentException, IllegalAccessException {
		Map<String, Object> m = new HashMap<String, Object>();
		entity2Map(o, m, isCamel);
		
		return map2Condition(m);
	}
	
	/**
	 * map转换成条件对象列表
	 * @param m
	 * @return
	 */
	public static List<Condition> map2Condition(Map<String, Object> m) {
		List<Condition> conditionList = new ArrayList<Condition>(m.size());
		Set<Entry<String,Object>> entrySet = m.entrySet();
		Condition conditionBean = null;
		for (Entry<String, Object> entry : entrySet) {
			if(entry.getValue() != null) {
				conditionBean = new Condition(entry.getKey(), entry.getValue()); 
				conditionList.add(conditionBean);
			}
		}
		return conditionList;
	}
	
	/**
	 * 从实体类对象中获取表名
	 * @param o 实体类对象
	 * @param isCamel 是否做camel命名规则转换
	 * @return
	 */
	public static <T> String getTableNameFromEntity(Class<T> c, boolean isCamel) {
		String tableName = "";
		if(c.isAnnotationPresent(Table.class)) {
			Table tableAnno = c.getDeclaredAnnotation(Table.class);
			tableName = tableAnno.tableName();
			if(StringUtils.isBlank(tableName)) {
				tableName = c.getSimpleName();
				if(isCamel && StringUtils.isNotBlank(tableName)) {
					tableName = camel2Underline(tableName);
				}
			}
		}
		return tableName;
	}
	
	/**
	 * 获取主键字段值
	 * 		局限性：没有考虑联合主键的获取，没有考虑父类中定义PK的情况
	 * @param c
	 * @param isCamel
	 */
	public static <T> String getPkColumnName(Class<T> c, boolean isCamel) {
		String pkColumnName = "";
		Field[] declaredFields = c.getDeclaredFields();
		for (Field field : declaredFields) {
			if(field.isAnnotationPresent(PK.class)) {
				if(field.isAnnotationPresent(Column.class)) {
					Column columnAnno = field.getDeclaredAnnotation(Column.class);
					pkColumnName = columnAnno.columnName();
					if(StringUtils.isBlank(pkColumnName)) {
						pkColumnName = camel2Underline(field.getName());
						break;
					}
				}
			}
		}
		
		return pkColumnName;
	}
	
	/**
	 * 骆驼命名转换下划线命名
	 * @param s
	 * @return
	 */
	public static String camel2Underline(String s) {
		if(StringUtils.isNotBlank(s)) {
			if(s.indexOf("_")==-1){
				for (int i = 0; i < s.length(); i++) {
					char c = s.charAt(i);
					if (Character.isUpperCase(c)) {
						if(i > 0) {
							s = new String(s.replace(String.valueOf(c), "_" + (char) (c + 32)));
						} else {
							s = new String(s.replace(String.valueOf(c), String.valueOf((char) (c + 32))));
						}
					}
				}
			}
		}
		return s;
	}
	
	/**
	 * 下划线命名转换骆驼命名
	 * @param s
	 * @return
	 */
	public static String underline2Camel(String s) {
		if(StringUtils.isNotBlank(s)) {
			int index;
			while ((index = s.indexOf("_")) != -1) {
				char lower = s.charAt(index + 1);
				char upper = (char) (lower - 32);
				s = new String(s.replace("_" + lower, "" + upper));
			}
		}
		return s;
	}
	
	/**
	 * 获取字段名
	 * @param field
	 * @param isCamel
	 * @return
	 */
	private static String getColumnName(Field field, boolean isCamel) {
		String columnName = "";
		if(field.isAnnotationPresent(Column.class)) {
			Column columnAnno = field.getDeclaredAnnotation(Column.class);
			columnName = columnAnno.columnName();
		}
		if(StringUtils.isBlank(columnName)) {
			columnName = field.getName();
			if(isCamel) {
				columnName = camel2Underline(columnName);
			}
		}
		return columnName;
	}
	
}
